﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nature.Numerics
{
    public interface IVector : IEnumerable<double>
    {
        double this[int i] { get; set; }

        int Length { get; }
    }

    public static partial class ExtensionsIVector
    {
        public static double[] ToArray(this IVector vector)
        {
            double[] array = new double[vector.Length];
            for (int i = 0; i < array.Length; ++i)
            {
                array[i] = vector[i];
            }
            return array;
        }

        public static double Max<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double m = dim[0];
            double t;
            for (int i = 1; i < dim.Length; ++i)
            {
                t = dim[i];
                if (m < t) { m = t; }
            }
            return m;
        }


        public static void Add<T>(this double[] lhs, T rhs) where T : IVector
        {
            for (int i = 0; i < lhs.Length; ++i)
            {
                lhs[i] += rhs[i];
            }
        }

        public static double Min<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double m = dim[0];
            double t;
            for (int i = 1; i < dim.Length; ++i)
            {
                t = dim[i];
                if (m > t) { m = t; }
            }
            return m;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <remarks>
        /// This method may not be renamed into Sum since this would result in a number
        /// of ambiguous method references clashing with the IEnumerable extensions
        /// </remarks>
        public static double VSum<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double s = dim[0];
            for (int i = 1; i < dim.Length; ++i)
            {
                s += dim[i];
            }
            return s;
        }

        public static double Norm<T>(this T dim) where T : IVector
        {
            return Math.Sqrt(dim.NormSqr());
        }

        public static double NormSqr<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double m = dim[0];
            double s = m * m;
            for (int i = 1; i < dim.Length; ++i)
            {
                m = dim[i];
                s += m * m;
            }
            return s;
        }

        public static double AMax<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double m = Math.Abs(dim[0]);
            double t;
            for (int i = 1; i < dim.Length; ++i)
            {
                t = Math.Abs(dim[i]);
                if (m < t) { m = t; }
            }
            return m;
        }


        public static double AMin<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double m = Math.Abs(dim[0]);
            double t;
            for (int i = 1; i < dim.Length; ++i)
            {
                t = Math.Abs(dim[i]);
                if (m > t) { m = t; }
            }
            return m;
        }



        public static double ASum<T>(this T dim) where T : IVector
        {
            if (0 == dim.Length) { return double.NaN; }
            double s = Math.Abs(dim[0]);
            for (int i = 1; i < dim.Length; ++i)
            {
                s += Math.Abs(dim[i]);
            }
            return s;
        }

        public static double Max<T>(this T dim, out int index) where T : IVector
        {
            if (0 == dim.Length) { index = -1; return double.NaN; }
            int imax = 0;
            double m = dim[0];
            double t;

            for (int i = 1; i < dim.Length; ++i)
            {
                t = dim[i];
                if (m < t) { imax = i; m = t; }
            }
            index = imax;
            return m;
        }

        public static double Min<T>(this T dim, out int index) where T : IVector
        {
            if (0 == dim.Length) { index = -1; return double.NaN; }
            int imin = 0;
            double m = dim[0];
            double t;

            for (int i = 1; i < dim.Length; ++i)
            {
                t = dim[i];
                if (m > t) { imin = i; m = t; }
            }
            index = imin;
            return m;
        }



        public static double AMax<T>(this T dim, out int index) where T : IVector
        {
            if (0 == dim.Length) { index = -1; return double.NaN; }
            int imax = 0;
            double m = Math.Abs(dim[0]);
            double t;

            for (int i = 1; i < dim.Length; ++i)
            {
                t = Math.Abs(dim[i]);
                if (m < t) { imax = i; m = t; }
            }
            index = imax;
            return m;
        }

        public static double AMin<T>(this T dim, out int index) where T : IVector
        {
            if (0 == dim.Length) { index = -1; return double.NaN; }
            int imin = 0;
            double m = Math.Abs(dim[0]);
            double t;

            for (int i = 1; i < dim.Length; ++i)
            {
                t = Math.Abs(dim[i]);
                if (m > t) { imin = i; m = t; }
            }
            index = imin;
            return m;
        }

        public static void Assign<T>(this T dim, double val) where T : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] = val;
            }
        }

        public static void Add<T>(this T dim, double val) where T : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] += val;
            }
        }


        public static void Subtrac<T>(this T dim, double val) where T : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] -= val;
            }
        }

        public static void Multiply<T>(this T dim, double val) where T : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] *= val;
            }
        }

        public static void Devide<T>(this T dim, double val) where T : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] /= val;
            }
        }

        public static void Assign<T, U>(this T dim, U val)
            where T : IVector
            where U : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] = val[i];
            }
        }



        public static void Add<T, U>(this T dim, U val)
            where T : IVector
            where U : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] += val[i];
            }
        }

        public static void Subtrac<T, U>(this T dim, U val)
            where T : IVector
            where U : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] -= val[i];
            }
        }

        public static void Multiply<T, U>(this T dim, U val)
            where T : IVector
            where U : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] *= val[i];
            }
        }

        public static void Devide<T, U>(this T dim, U val)
            where T : IVector
            where U : IVector
        {
            for (int i = 0; i < dim.Length; ++i)
            {
                dim[i] /= val[i];
            }
        }

    }
}
